iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
SideProject30

世界第一簡單的UEFI,實作打造自己的開機畫面系列 第 25

【Day 25】自己寫的UEFI開機畫面成功進入Boot Menu!

  • 分享至 

  • xImage
  •  

嗨我是k66,延續前篇Day24續寫Entry.c,前篇卡讀使用者按鍵,我發現問題在於WaitForEvent(),其實在while(1)內可以不用Wait,僅使用ReadKeyStroke()就能達成讀使用者按鍵。一樣先放成果圖,再放程式碼。
https://ithelp.ithome.com.tw/upload/images/20231001/20161828dcNlFuvDNB.png
按B進入Boot Menu(Boot Menu製作細節參考Day23)
https://ithelp.ithome.com.tw/upload/images/20231001/201618289L8SP2TThK.png

放碼上來!


受限於篇幅,本篇僅放完整Entry.c,其他檔案請參考前篇Day24

  • Entry.c
    主要改: ShowHomeMenu()的While迴圈,與新增BootMenu()。
#include<Entry.h>

EFI_STATUS ConvertBmpToBlt(IN VOID *BmpImage, IN UINTN BmpImageSize, IN OUT VOID **GopBlt, IN OUT UINTN *GopBltSize, OUT UINTN *PixelHeight, OUT UINTN *PixelWidth)
{

	UINT8                         *ImageData;
	UINT8                         *ImageBegin;
	BMP_IMAGE_HEADER              *BmpHeader;
	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt;
	UINT64                        BltBufferSize;
	UINTN                         Height;
	UINTN                         Width;
	UINTN                         ImageIndex;

	BmpHeader = (BMP_IMAGE_HEADER *)BmpImage;
	ImageBegin = ((UINT8 *)BmpImage) + BmpHeader->ImageOffset;

	if (BmpHeader->BitPerPixel != 24 && BmpHeader->BitPerPixel != 32)
		return EFI_UNSUPPORTED;

	BltBufferSize = BmpHeader->PixelWidth * BmpHeader->PixelHeight * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
	*GopBltSize = (UINTN)BltBufferSize;
	*GopBlt = AllocatePool(*GopBltSize);   
	if (*GopBlt == NULL)
	{
		return EFI_OUT_OF_RESOURCES;
	}

	*PixelWidth = BmpHeader->PixelWidth;
	*PixelHeight = BmpHeader->PixelHeight;

	ImageData = ImageBegin;
	BltBuffer = *GopBlt;
	for (Height = 0; Height < BmpHeader->PixelHeight; Height++)
	{
		Blt = &BltBuffer[(BmpHeader->PixelHeight - Height - 1) * BmpHeader->PixelWidth];
		for (Width = 0; Width < BmpHeader->PixelWidth; Width++, Blt++)
		{
			switch (BmpHeader->BitPerPixel)
			{
			case 24:
				Blt->Blue = *ImageData++;
				Blt->Green = *ImageData++;
				Blt->Red = *ImageData++;
				break;
			case 32:
				ImageData++;
				Blt->Blue = *ImageData++;
				Blt->Green = *ImageData++;
				Blt->Red = *ImageData++;
				break;
			default:
				break;
			}
		}

		ImageIndex = (UINTN)(ImageData - ImageBegin);
		if ((ImageIndex % 4) != 0)
			ImageData = ImageData + (4 - (ImageIndex % 4));
	}
	
	return EFI_SUCCESS;
}


void ShowCurrTime()
{
    UINT8 sec=0;
    UINT8 min=0;
    UINT8 hour=0;
    // UINT8 weekday=0;
    UINT8 day=0;
    UINT8 month=0;
    UINT8 year=0;
    
	IoWrite8(CMOS_ADDRESS,0x00);// sec, 0x00
	sec=IoRead8(CMOS_DATA);
	IoWrite8(CMOS_ADDRESS,0x02);// min, 0x02
	min=IoRead8(CMOS_DATA);
	IoWrite8(CMOS_ADDRESS,0x04);// hour, 0x04
	hour=IoRead8(CMOS_DATA);
	// IoWrite8(CMOS_ADDRESS,0x06);// weekday, 0x06
	// weekday=IoRead8(CMOS_DATA);
	IoWrite8(CMOS_ADDRESS,0x07);// day, 0x07
	day=IoRead8(CMOS_DATA);
	IoWrite8(CMOS_ADDRESS,0x08);// month, 0x08
	month=IoRead8(CMOS_DATA);
	IoWrite8(CMOS_ADDRESS,0x09);// year, 0x09
	year=IoRead8(CMOS_DATA);
	Print(L"| Curr time: %02x/%02x/%02x  %02x:%02x:%02x |\n",year,month,day,hour,min,sec);
}

void ShowGraphicMode()
{
	Print(L"|    Graphic Mode: 1920x1080    |\n");
}


static void BootMenu()
{
    // 設定字為red(0x04),並且清除畫面
    gST -> ConOut -> SetAttribute(gST->ConOut,0x4);
    gST -> ConOut -> ClearScreen(gST->ConOut);
    Print(L"*******************************\n");
    Print(L"|      Boot Menu              |\n");
    Print(L"|-----------------------------|\n");
    Print(L"|                             |\n");
    Print(L"|    Press 1 into Kernel 1    |\n");
    Print(L"|                             |\n");
	Print(L"|    Press 2 into Kernel 2    |\n");
    Print(L"|                             |\n");
    Print(L"*******************************\n");
    
    // int (*KernelEntry)();
    // KernelEntry = (int (*)() )KernelEntryPoint;
    // Print(L"Jump to Kernel Entry Point");
    // int KN = KernelEntry();
    // Print(L"Kernel Entry Value = %d \n", KN);
    // Select one of all kernels
    EFI_INPUT_KEY key;
    gST->ConIn->ReadKeyStroke(gST->ConIn, &key);
    while(1)
    {
        gST->ConIn->ReadKeyStroke(gST->ConIn, &key);
        if(key.UnicodeChar == L'1' || key.UnicodeChar == L'2' )
        {
			gST -> ConOut -> ClearScreen(gST->ConOut); // 清除畫面
			Print(L"[Press B] ENTER BOOT MENU...\n");
			gST -> ConOut -> SetAttribute(gST->ConOut,0x4); // 設定為紅字
			if(key.UnicodeChar == L'1')
				Print(L"Entry Kernel 1....");
			else
				Print(L"Entry Kernel 2....");
            // Kernel(key.UnicodeChar);
            break;
        }
        
    }
}

void ShowHomeMenu()
{
	// 清除畫面並且設定字為白色(0x0F)
    gST -> ConOut -> ClearScreen(gST->ConOut);
	gST -> ConOut -> SetAttribute(gST->ConOut,0x0F);
    Print(L"*********************************\n");
    Print(L"|    OinkBootLoader  v0.0       |\n");
	Print(L"|    CopyRight 2023 k66         |\n");
	Print(L"|                               |\n");
	Print(L"|                               |\n");
	Print(L"|-------------------------------|\n");
	ShowCurrTime();
	ShowGraphicMode();
	Print(L"|    UEFI Version: 2.9          |\n");
    Print(L"|-------------------------------|\n");
    Print(L"|    Press B for BOOT MENU      |\n");
    Print(L"|    Press S for SETUP          |\n");
    Print(L"|    Press Q for SHUTDOWN       |\n");
    Print(L"*********************************\n");
}

EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE* SystemTable)
{
	EFI_STATUS Status;
	EFI_GRAPHICS_OUTPUT_PROTOCOL * GraphicsProtocol = NULL;
	// EFI_GRAPHICS_OUTPUT_BLT_PIXEL BLACK = { 0x00, 0x00, 0x00, 0 };
	// UINTN EventIndex;
	// EFI_EVENT          KeyEvent;
	// EFI_EVENT          WaitList[2];
	EFI_INPUT_KEY Key;
	UINTN			  x=600, y=100; //欲顯示Logo.bmp的左上角x,y座標
	VOID			  *GopBlt = NULL;
	UINTN			  GopBltSize;
	UINTN			  BmpHeight;
	UINTN			  BmpWidth;
	EFI_FILE_PROTOCOL *Root = 0;
	EFI_FILE_PROTOCOL *NewHandle = 0;
	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
	EFI_FILE_INFO *fileinfo;
	VOID * Buffer;
	UINTN BufferSize;
	// IMAGE * ImageList = NULL;

	UINTN infosize = SIZE_OF_EFI_FILE_INFO;
	EFI_GUID info_type = EFI_FILE_INFO_ID;

	/* 檔案處理 */
	/* 四步: 1.LocateProtocol 2.OpenVolume 3.Image Open 4.AllocatePool  */
	Status = gBS->LocateProtocol(&gEfiSimpleFileSystemProtocolGuid, NULL, (VOID**)&SimpleFileSystem);
	if (EFI_ERROR(Status))
	{
		Print(L"ERROR (Entry.c): LocateProtocol\n");
		return Status;
	}

	Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &Root);
	if (EFI_ERROR(Status))
	{
		Print(L"ERROR (Entry.c): OpenVolume \n");
		return Status;
	}

	Status = Root->Open(Root, &NewHandle, (CHAR16*)L"Logo.bmp", EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0);
	if (EFI_ERROR(Status))
	{
		Print(L"ERROR (Entry.c): Image Open \n");
		return Status;
	}

	
	Status = gBS->AllocatePool(AllocateAnyPages, infosize, (VOID **)&fileinfo);
	if (EFI_ERROR(Status))
	{
		Print(L"ERROR (Entry.c): AllocatePool \n");
		return Status;
	}

	Status = NewHandle->GetInfo(NewHandle, &info_type, &infosize, NULL);
	Status = NewHandle->GetInfo(NewHandle, &info_type, &infosize, fileinfo);
	if (EFI_ERROR(Status))
	{
		Print(L"ERROR (Entry.c): Get Informations\n");
		return Status;
	}

	/* Read Buffer&BufferSize for converting BMP to Blt */
	BufferSize = fileinfo->FileSize;
	gBS->AllocatePool(AllocateAnyPages, BufferSize, (VOID **)&Buffer);
	if (EFI_ERROR(Status))
	{
		Print(L"ERROR (Entry.c): AllocatePool\n");
		return Status;
	}

	/* Buffer處理 */
	Status = NewHandle->Read(NewHandle, &BufferSize, Buffer);
	if (EFI_ERROR(Status))
	{
		Print(L"ERROR (Entry.c): Read Buffer\n");
		return Status;
	}

	/* 關閉檔案 */
	Status = NewHandle->Close(NewHandle);
	if (EFI_ERROR(Status))
	{
		Print(L"ERROR (Entry.c): Close \n");
		return Status;
	}

	/* Convert BMP to Blt */
	Status = ConvertBmpToBlt(Buffer, BufferSize, &GopBlt, &GopBltSize, &BmpHeight, &BmpWidth);
	if (EFI_ERROR(Status))
	{
		Print(L"ERROR (Entry.c): ConvertBmpToBlt\n");
		return Status;
	}


	/* GraphicsProtocol */
	Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID**)&GraphicsProtocol);
	if (EFI_ERROR(Status))
	{
		Print(L"ERROR (Entry.c): LocateProtocol\n");
		return Status;
	}

	/* Show Home menu & Logo.bmp */
	ShowHomeMenu();
	Status = GraphicsProtocol->Blt(GraphicsProtocol, GopBlt, EfiBltBufferToVideo, 0, 0, x, y, BmpWidth, BmpHeight, 0); // x,y是欲顯示之Logo.bmp左上角座標
	// EFI_GRAPHICS_OUTPUT_BLT_PIXEL White = { 0xFF, 0xFF, 0xFF, 0 };
	// Status = GraphicsProtocol->Blt(GraphicsProtocol, &White, EfiBltVideoFill, 0, 0, x, y, 1, 1, 0);
	// MicroSecondDelay(100000*10000); // Force to stay
	
	while(1)
	{
		Status = gST->ConIn->ReadKeyStroke(gST->ConIn, &Key);
		
		switch (Key.UnicodeChar)
		{
		case 'B': // BOOT MENU
			gST -> ConOut -> ClearScreen(gST->ConOut); // 清除畫面
			Print(L"[Press B] ENTER BOOT MENU...\n");
			gST -> ConOut -> SetAttribute(gST->ConOut,0x4); // 設定字為紅色(0x04)
			goto BOOTMENU;
			break;
		case 'S': // SETUP
			gST -> ConOut -> ClearScreen(gST->ConOut); // 清除畫面
			Print(L"[Press S] SETUP\n");
			gST -> ConOut -> SetAttribute(gST->ConOut,0x2); // 設定字為綠色(0x02)
			break;
		case 'Q': // Q
			gST -> ConOut -> ClearScreen(gST->ConOut); // 清除畫面
			Print(L"[Press Q] QUIT\n");
			goto SHUTDOWN;
			break;
		}

		// MicroSecondDelay(10000*10000); // Force to stay
	}

	SHUTDOWN:
		Print(L"--- SHUTDOWN: User press ESC ---\n");
	BOOTMENU:
		BootMenu();
	
	// Free File&Buffer
	gBS->FreePool(fileinfo);
	gBS->FreePool(Buffer);

	return EFI_SUCCESS;
}

總結

本篇完成Entry.c進入Boot Menu部分,也將前篇卡住的讀使用者按鍵問題解決,接下來幾篇會完成Kernel部分。我們明天見!


上一篇
【Day 24】自己寫的UEFI開機畫面終於成形!
下一篇
【Day 26】自己寫的UEFI開機畫面—設定螢幕解析度
系列文
世界第一簡單的UEFI,實作打造自己的開機畫面31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言